package com.bite.system.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bite.common.core.constants.Constants;
import com.bite.common.core.enums.ResultCode;
import com.bite.common.security.exception.ServiceException;
import com.bite.system.domain.exam.Exam;
import com.bite.system.domain.exam.ExamQuestion;
import com.bite.system.domain.exam.dto.*;
import com.bite.system.domain.exam.vo.ExamDetailVO;
import com.bite.system.domain.exam.vo.ExamVO;
import com.bite.system.domain.question.Question;
import com.bite.system.domain.question.vo.QuestionVO;
import com.bite.system.manager.ExamCacheManager;
import com.bite.system.manager.UserCacheManager;
import com.bite.system.mapper.exam.ExamMapper;
import com.bite.system.mapper.exam.ExamQuestionMapper;
import com.bite.system.mapper.exam.ExamUserGroupMapper;
import com.bite.system.mapper.question.QuestionMapper;
import com.bite.system.mapper.user.UserExamMapper;
import com.bite.system.mapper.user.UserGroupUserMapper;
import com.bite.system.service.exam.IExamService;
import jakarta.validation.constraints.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
* @author 17450
* @description 针对表【tb_exam】的数据库操作Service实现
* @createDate 2025-05-18 15:20:13
*/
@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper, ExamQuestion>
    implements IExamService {

    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private ExamQuestionMapper examQuestionMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private ExamCacheManager examCacheManager;

    @Autowired
    private UserExamMapper userExamMapper;

    @Autowired
    private UserCacheManager userCacheManager;
    @Autowired
    private UserGroupUserMapper userGroupUserMapper;
    @Autowired
    private ExamUserGroupMapper examUserGroupMapper;

    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
//        PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());
        return examMapper.list(examQueryDTO);
    }

    @Override
    public Long add(ExamAddDTO examAddDTO) {
        // 先判断参数是不是符合
        checkOnSaveExam(examAddDTO, null);
        // 转换
        Exam exam = new Exam();
        BeanUtil.copyProperties(examAddDTO, exam);
        examMapper.insert(exam);
        return exam.getExamId();
    }

    @Override
    public boolean questionAdd(ExamQuestionAddDTO examQuestionAddDTO) {
        LinkedHashSet<Long> questionIds = examQuestionAddDTO.getQuestionIds();
        Long examId = examQuestionAddDTO.getExamId();
        // 先校验参数
        if (CollectionUtil.isEmpty(questionIds)) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }
        Exam exam = getExam(examId);
        // 发布的时候不能修改
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_PUBLISH);
        }
        checkExamBeforeSave(exam);
        // 校验题目id 是否存在
        List<Question> questions = questionMapper.selectBatchIds(questionIds);
        if (CollectionUtil.isEmpty(questions) || questions.size() < questionIds.size()) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_ILLEGAL);
        }

        List<ExamQuestion> oldQuestions = examQuestionMapper.selectList(new LambdaQueryWrapper<>(ExamQuestion.class)
                .select(ExamQuestion::getQuestionId)
                .eq(ExamQuestion::getExamId, examId));


        // 先去重 再添加
        LinkedHashSet<Long> noRepeatQuestionIds = getNoRepeatQuestionIds(oldQuestions, questionIds);
        // 添加 TODO oldQuestions.size() + 1 不一定是在数据库中最大的（如果按顺序存储自然可以获取到 但是如果要修改导致优先级不一样 那就只能从数据库中获取了）
        int startOrder = oldQuestions.size() + 1;
        saveBatch(noRepeatQuestionIds, examId, startOrder);
        return true;
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        Exam exam = getExam(examId);
        checkExamBeforeSave(exam);
        // 发布的时候不能修改
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_PUBLISH);
        }
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId)
                .eq(ExamQuestion::getQuestionId, questionId));
    }

    // 在编辑的之前查看合不合法
    private void checkExamBeforeSave(Exam exam) {
        // 已经发布并且时间已经达到开赛时间
        if (exam.getStartTime().isBefore(LocalDateTime.now())
                && exam.getStatus().equals(Constants.TRUE)) {
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

    @Override
    public ExamDetailVO detail(Long examId) {
        // 获取基本信息
        ExamDetailVO result = new ExamDetailVO();
        Exam exam = getExam(examId);
        BeanUtil.copyProperties(exam, result);

        // 获取ids
        List<ExamQuestion> examQuestions = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>()
                .select(ExamQuestion::getQuestionId, ExamQuestion::getQuestionOrder)
                .eq(ExamQuestion::getExamId, examId)
                .orderByDesc(ExamQuestion::getQuestionOrder)
        );
        // 没有相关题目
        if (CollectionUtil.isEmpty(examQuestions)) {
            return result;
        }

        List<Long> questionIds = examQuestions.stream().map(ExamQuestion::getQuestionId).toList();
        // 需要重新排序一下 因为MyBatis-Plus 的顺序不是按照examQuestions顺序来排序的
        // 获取相关信息
//        List<Question> questions = questionMapper.selectList(new LambdaQueryWrapper<Question>()
//                .select(Question::getQuestionId, Question::getTitle, Question::getDifficulty)
//                .in(Question::getQuestionId, questionIds)
//        );
        List<Question> questions = questionMapper.selectListOrderByAscExamQuestion(questionIds, examId);

        List<QuestionVO> questionVOList = BeanUtil.copyToList(questions, QuestionVO.class);
        result.setQuestionVOList(questionVOList);

        return result;
    }

    @Override
    public int edit(ExamEditDTO examEditDTO) {
        Exam exam = getExam(examEditDTO.getExamId());
        // 发布的时候不能修改 TODO 当更新的时候会改变题目顺序（移动到最后）
        checkExamBeforeSave(exam);
        if (!exam.getType().equals(examEditDTO.getType())) {
            throw new ServiceException(ResultCode.EXAM_TYPE_CHANGE);
        }

        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_PUBLISH);
        }
        checkOnSaveExam(examEditDTO, exam.getExamId());
        BeanUtil.copyProperties(examEditDTO, exam);
        // 更新
        return examMapper.updateById(exam);
    }


    @Override
    @Transactional
    public int delete(Long examId) {
        Exam exam = getExam(examId);
        checkExamBeforeSave(exam);
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));

        return examMapper.deleteById(examId);
    }

    @Override
    public int publish(Long examId) {
        Exam exam = getExam(examId);
        checkExamBeforeSave(exam);
        if (LocalDateTime.now().isAfter(exam.getStartTime())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);
        }
        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<>(ExamQuestion.class)
                .eq(ExamQuestion::getExamId, examId));
        if (count == null || count <= 0) {
            throw new ServiceException(ResultCode.EXAM_NOT_QUESTION);
        }
        exam.setStatus(Constants.TRUE);
        examCacheManager.addCache(exam);
        userCacheManager.addUserExamWithEntered(examId);
        return examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        Exam exam = getExam(examId);
        checkExamBeforeSave(exam);
        exam.setStatus(Constants.FALSE);
//        userExamMapper.deleteByExamId(examId);
        userCacheManager.delUserExamWithEntered(examId);
        examCacheManager.deleteCache(examId);
        return examMapper.updateById(exam);
    }

    @Override
    public List<String> userGroupIdByExamId(Long examId) {
        if (examId == null) throw new ServiceException(ResultCode.EXAM_NOT_EXISTS);
        return examUserGroupMapper.selectUserGroupIdByExamIdList(List.of(examId))
                .stream().map(Object::toString).toList();
    }

    private Exam getExam(Long examId) {
        Exam exam = examMapper.selectById(examId);
        if (exam == null) {
            throw new ServiceException(ResultCode.EXAM_NOT_EXISTS);
        }
        return exam;
    }

    // 当保存（添加与修改）的时候检查参数是否合法
    private void checkOnSaveExam(ExamAddDTO examAddDTO, Long examId) {
        if (examAddDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);
        }
        if (examAddDTO.getEndTime().isBefore(examAddDTO.getStartTime())) {
            throw new ServiceException(ResultCode.EXAM_END_TIME_AFTER_START_TIME);
        }
        // 判断名字是否相同(只有在标题一样并且id不一样的时候获取到)
        List<Exam> exams = examMapper.selectList(new LambdaQueryWrapper<>(Exam.class)
                .select(Exam::getTitle)
                .eq(Exam::getTitle, examAddDTO.getTitle())
                .ne(examId != null, Exam::getExamId, examId)
        );
        if (CollectionUtil.isNotEmpty(exams)) {
            throw new ServiceException(ResultCode.EXAM_TITLE_REPEAT);
        }
    }

    // 保存竞赛与题目的关系
    private boolean saveBatch(LinkedHashSet<Long> noRepeatQuestionIds, @NotNull(message = "竞赛Id不能为空") Long examId, int startOrder) {
        AtomicInteger order = new AtomicInteger(startOrder);
        return saveBatch(noRepeatQuestionIds.stream().map(questionId -> {
            return new ExamQuestion(examId, questionId, order.getAndIncrement());
        }).toList());
    }

    // 获取到没有添加的QuestionIds
    private LinkedHashSet<Long> getNoRepeatQuestionIds(Set<ExamQuestion> oldQuestions, @NotNull(message = "题目列表不能为空") LinkedHashSet<Long> questionIds) {
        Set<Long> collect = oldQuestions.stream().map(ExamQuestion::getQuestionId).collect(Collectors.toSet());
        return questionIds.stream().filter(newId -> {
            return !collect.contains(newId);
        }).collect(Collectors.toCollection(LinkedHashSet::new));
    }

    private LinkedHashSet<Long> getNoRepeatQuestionIds(List<ExamQuestion> oldQuestions, @NotNull(message = "题目列表不能为空") LinkedHashSet<Long> questionIds) {
        return getNoRepeatQuestionIds(new HashSet<>(oldQuestions), questionIds);
    }
}




